{// class game2048{
    //    constructor(){
    //        // 类声明的时候会用到的函数
    //        console.log(this.init); //先有函数
    //        console.log(this.abc) ; //undefined 

    //        this.abc=123
    //    }
    //    // 这里是写函数的位置
    //    init(){
    //        console.log('init函数')
    //    }
    // }
    // var a = new game2048(); //返回一个对象
    ///////////////////////////////////////////
    // 内部的大概实现
    // a={};
    // 把所有函数都放到a身上
    // a == {
    //     init:fn
    // }
    // 在函数放上去之后就会执行constructor
    // 注意 constructor函数执行的时候 this一定指向于a



    //剩下的18
    //  标签元素第一个要认识的
    //  标签元素.offsetLeft 表示子元素左边框和父元素左边框的距离
    //  标签元素.offsetTop 表示子元素上边框和父元素上边框的距离

}

class Data {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}
class Game2048 {
    constructor() {
        // this -> 实例化对象a
        this.arr = [];
        //你们发挥的地方 获取元素的坐标
        // console.log(document.querySelectorAll('.kuai')[15].offsetLeft);
        // console.log(document.querySelectorAll('.kuai')[15].offsetTop);
        let num = document.querySelectorAll('.kuai');
        this.father = document.querySelector('#background_box');
        this.init(num);
        this.index = 0; //用来记录当前生成了多少个元素
        this.random();
        this._score = 0
        this.score_box = document.querySelector('.score');
        this.beast_score_box = document.querySelector('.score.beast');
        // <!-- 首先在网页打开或者刷新的时候 显示最好分数 -->
        num = localStorage.getItem('beast_score');
        if (num == null) {
            this.beast_score = 0
        } else {
            this.beast_score = num;
        }
        this.beast_score_box.innerText = '最好分数：' + this.beast_score
    }
    set score(val) {
        // <!-- 当当前分数大于最好分数的时候 2个div的值要同时刷新 -->
        this._score = val;
        this.score_box.innerText = '分数:' + val;
        localStorage.setItem('score', JSON.stringify(val));
        if (val > this.beast_score) {
            localStorage.setItem('beast_score', JSON.stringify(val));
            this.beast_score = val
            this.beast_score_box.innerText = '最好分数：' + this.beast_score
        }
    }
    get score() {
        return this._score;
    }
    init(num) {
        for (let index = 0; index < num.length; index++) {
            var x = num[index].offsetLeft
            var y = num[index].offsetTop
            this.arr[index] = new Data(x, y);
            this.arr[index].index = 0;
            this.arr[index].is = false;
        }
    }
    add(index) {
        { //这个函数接受一个index的值 index值表示从左到右 从上到下 由0开始
            // 提示 
            // this.arr[index].x  this.arr[index].y 就是我们的一个坐标
            // this指向于a
            // constructor() {
            //  this.father=document.querySelector('#background_box');
            // }
            //你要创建一个box类名的元素 将他加入到id为background_box的盒子内部
        }
        if (this.arr[index].ele) {
            console.log('已经有格子了 不能在这里添加')
            return
        }
        // 浏览器渲染页面的时候有2种
        //  重排 css样式属性变化的时候 涉及布局变化 就会触发浏览的一个重排 把整一个页面的排版重新计算一遍
        //  重绘 不会影响当前布局 css样式变化的时候 类似的背景颜色变化
        // 这些样式改变会触发重绘
        // 浏览为了优化 重绘在一定时间内只会执行一次
        // 重排会强制的触发重绘
        var a = document.createElement('div');
        a.classList.add('box');
        a.style.top = this.arr[index].y + 'px';
        a.style.left = this.arr[index].x + 'px';
        a.style.transform = 'scale(0)';
        this.arr[index].ele = a;
        var number = Math.round(Math.random());
        if (number == 1) {
            this.arr[index].index = 4
        } else {
            this.arr[index].index = 2
        }
        this.change(this.arr[index]);
        // 把创建的标签存入数组对应的位置
        this.father.append(a);
        this.index += 1;
        // setTimeout(fn,time)
        // fn 表示时间到了之后要执行的函数 ,time单位ms等待时间是多少
        //这这个位置上通过获取某个元素的宽度来强制触发重排
        //从而触发重绘
        document.querySelector('.kuai').clientWidth;
        a.style.transform = 'scale(1)'
        // 要求刚好覆盖 下标为index的盒子

    }
    random() {
        // 在  constructor 添加一行 this.index=0
        // 在 add() 里面 在append之后 添加 this.index+=1
        if (this.index == 16) {
            console.log('已经满了无法随机添加');
            return
        }
        // 随机0-15数字出来
        Math.random() * 15;
        // 不需要任何参数 [0-1) 取不到1
        // 返回 [0-1)的浮点数
        // Math.round(number);
        // 将传入的数字 四舍五入到整数
        var num = Math.round(Math.random() * 15);
        while (this.arr[num].ele) {
            // 如果这里已经有元素在了我们就重新生成一个num
            console.log('随机到一个相同的重新生成')
            num = Math.round(Math.random() * 15);
        }
        // 能够获取 [0-15]整数
        this.add(num)
    }
    control(e) {

        switch (e.key) {
            case 'ArrowLeft':
                console.log('左');
                for (let x = 0; x <= 3; x++) {
                    for (let y = 0; y <= 3; y++) {
                        this.move(x, y, 0)
                    }
                }
                break;           //依照左边的写法完善下上右
            case 'ArrowRight':
                console.log('右')
                for (let x = 3; x >= 0; x--) {
                    for (let y = 0; y <= 3; y++) {
                        this.move(x, y, 1)
                    }
                }
                break;
            case 'ArrowUp':
                console.log('上')
                for (let x = 0; x <= 3; x++) {
                    for (let y = 0; y <= 3; y++) {
                        this.move(x, y, 2)
                    }
                }
                break;
            case 'ArrowDown':
                console.log('下')
                for (let x = 0; x <= 3; x++) {
                    for (let y = 3; y >= 0; y--) {
                        this.move(x, y, 3)
                    }
                }
                break;
            default:
                break;
        }
        // todo
        for (var i = 0; i < this.arr.length; i++) {
            this.arr[i].is = false;
        }
        this.random();
		if (this.index>=16&& !this.check()) {
			console.log('游戏结束');
			this.game_over();
		}

    }
    move(x, y, type) {
        var a = this.getData(x, y)
        if (a == undefined || a.index == 0) {
            return
        }
        var _x = x, _y = y;// _x, _y;表示模块可以即将移动坐标
        var b;
        console.log(a);
        switch (type) {
            case 0:
                console.log('往左边移动');
                //想一下这么让块往左边移动 
                // 遇到墙壁或者块的时候要停下来
                while (b = this.getData(_x - 1, _y)) {
                    if (b.index == 0) {
                        _x -= 1;
                    } else {
                        // 遇到index不为0的情况下我们也要推出while
                        break;
                    }
                }
                break;
            case 1:
                console.log('往右边移动');
                while (b = this.getData(_x + 1, _y)) {
                    if (b.index == 0) {
                        _x += 1;
                    } else {
                        // 遇到index不为0的情况下我们也要推出while
                        break;
                    }
                }
                break;
            case 2:
                console.log('往上边移动');
                while (b = this.getData(_x, _y - 1)) {
                    if (b.index == 0) {
                        _y -= 1;
                    } else {
                        // 遇到index不为0的情况下我们也要推出while
                        break;
                    }
                }
                break;
            case 3:
                console.log('往下边移动');
                while (b = this.getData(_x, _y + 1)) {
                    if (b.index == 0) {
                        _y += 1;
                    } else {
                        // 遇到index不为0的情况下我们也要推出while
                        break;
                    }
                }
                break;
            default:
                break;
        }
        // 合并 如果index遇到相同的就合并起来
        if (b != undefined && b.index == a.index && b.is == false) {
            a.ele.style.left = b.x + 'px';
            a.ele.style.top = b.y + 'px';
            b.index = b.index * 2;
            this.score = this.score + b.index;
            b.is = true;
            b.ele.style.transform = 'scale(1.3)'
            a.index = 0;
            this.index--;
            var ele_a = a.ele;
            a.ele = null;
            var ele_b = b;
            // 你放了一个蛋糕在箱子里 并且定时2s后才过来吃
            // 在这2s中你的蛋糕被人替换成面粉
            // 2s之后 你过来吃 发现你吃下不是面包 而是面粉 这样子就是我们的bug所在
            setTimeout((function () {
                ele_a.remove();
                ele_b.ele.style.transform = 'scale(1)'
                this.change(ele_b);
            }).bind(this), 100)
        } else if (_x != x || _y != y) {
            // 第一个坑   解决第二按左边时候会覆盖墙角边的块的bug  
            b = this.getData(_x, _y);
            a.ele.style.top = b.y + 'px';
            a.ele.style.left = b.x + 'px';
            b.ele = a.ele;
            a.ele = null;
            b.index = a.index;
            a.index = 0;
            this.change(b);
        }
    }
    getData(x, y) {
        if (x < 0 || y < 0 || x > 3 || y > 3) {
            return undefined
        }
        return this.arr[x + y * 4]
    }
    change(obj) {
        // obj.index

        obj.ele.innerText = obj.index;
        switch (obj.index) {

            case 2: {
                obj.ele.style.color = "#7d716a"
                obj.ele.style.backgroundColor = "#f2e6e0"
                break;
            }
            case 4: {
                obj.ele.style.color = "#7b6d63"
                obj.ele.style.backgroundColor = "#eedfcd"
                break;
            }
            case 8: {
                obj.ele.style.color = "#fdf4ea"
                obj.ele.style.backgroundColor = "#f4b680"
                break;
            }
            case 16: {
                obj.ele.style.color = "#fdf8f3"
                obj.ele.style.backgroundColor = "#f3a16c"
                break;
            }
            case 32: {
                obj.ele.style.color = "#f2fceb"
                obj.ele.style.backgroundColor = "#f08f6c"
                break;
            }
            case 64: {
                obj.ele.style.color = "#fdf4ea"
                obj.ele.style.backgroundColor = "#f08f6c"
                break;
            }
            case 128: {
                obj.ele.style.color = "#f9f3fd"
                obj.ele.style.backgroundColor = "#e7d371"
                break;
            }
            case 256: {
                obj.ele.style.color = "#fff7e0"
                obj.ele.style.backgroundColor = "#f2cd61"
                break;
            }
            case 512: {
                obj.ele.style.color = "#fff9bc"
                obj.ele.style.backgroundColor = "#e7cb4e"
                break;
            }
            case 1024: {
                obj.ele.style.color = "#ffeeb9"
                obj.ele.style.backgroundColor = "#e6c62e"
                break;
            }
            case 2048: {
                obj.ele.style.color = "#fff8b9"
                obj.ele.style.backgroundColor = "#e1c521"
            }
            default:
                break;

        }
    }

    game_over() {
        //   完成游戏界面  
		document.querySelector('.game_over_box').style.display='flex';
		document.querySelector('.game_over_box>.button').onclick=this.try_again.bind(this);
    }
    try_again() {
        // 重新开始
		//console.log(555);
		for(var i=0;i<this.arr.length;i++){
			var a=this.arr[i];
			if (a.ele) {
				a.ele.remove();
			}
			a.index=0;
			a.ele=null;
			a.is=false;
		}
		this.score=0;
		this.index=0;
		document.querySelector('.game_over_box>.button').onclick=null;
		document.querySelector('.game_over_box').style.display='none';
    }
	check(){
		for (var x=0;x<4;x++) {
			for(var  y=0;y<4;y++){
				var element=this.getData(x,y);
				var ele=this.getData(x,y-1);
				if (ele!=undefined && ele.index==element.index) {
					return true;
				} 
				ele=this.getData(x+1,y);
				if(ele !=undefined &&ele.index==element.index){
					return true
				}
				ele=this.getData(x,y+1);
				if(ele !=undefined &&ele.index==element.index){
					return true
				}
				ele=this.getData(x-1,y);
				if(ele !=undefined &&ele.index==element.index){
					return true
				}
				
			}
		}
		
	}
	
}

var game = new Game2048();
document.onkeydown = game.control.bind(game)
//测试代码




